/*-------------------------------------------------------------------------
 *
 * buf_init.c
 *      buffer manager initialization routines
 *
 * Portions Copyright (c) 1996-2017, PostgreSQL Global Development Group
 * Portions Copyright (c) 1994, Regents of the University of California
 *
 * This source code file contains modifications made by THL A29 Limited ("Tencent Modifications").
 * All Tencent Modifications are Copyright (C) 2023 THL A29 Limited.
 *
 * IDENTIFICATION
 *      src/backend/storage/buffer/buf_init.c
 *
 *-------------------------------------------------------------------------
 */
#include "postgres.h"

#include "storage/bufmgr.h"
#include "storage/buf_internals.h"
#include "utils/guc.h"


BufferDescPadded *BufferDescriptors;
char       *BufferBlocks;
LWLockMinimallyPadded *BufferIOLWLockArray = NULL;
WritebackContext BackendWritebackContext;
CkptSortItem *CkptBufferIds;

#ifdef __OPENTENBASE__
bool        g_WarmSharedBuffer = false;
#endif
/*
 * Data Structures:
 *        buffers live in a freelist and a lookup data structure.
 *
 *
 * Buffer Lookup:
 *        Two important notes.  First, the buffer has to be
 *        available for lookup BEFORE an IO begins.  Otherwise
 *        a second process trying to read the buffer will
 *        allocate its own copy and the buffer pool will
 *        become inconsistent.
 *
 * Buffer Replacement:
 *        see freelist.c.  A buffer cannot be replaced while in
 *        use either by data manager or during IO.
 *
 *
 * Synchronization/Locking:
 *
 * IO_IN_PROGRESS -- this is a flag in the buffer descriptor.
 *        It must be set when an IO is initiated and cleared at
 *        the end of the IO.  It is there to make sure that one
 *        process doesn't start to use a buffer while another is
 *        faulting it in.  see WaitIO and related routines.
 *
 * refcount --    Counts the number of processes holding pins on a buffer.
 *        A buffer is pinned during IO and immediately after a BufferAlloc().
 *        Pins must be released before end of transaction.  For efficiency the
 *        shared refcount isn't increased if an individual backend pins a buffer
 *        multiple times. Check the PrivateRefCount infrastructure in bufmgr.c.
 */


/*
 * Initialize shared buffer pool
 *
 * This is called once during shared-memory initialization (either in the
 * postmaster, or in a standalone backend).
 */
void
InitBufferPool(void)
{// #lizard forgives
    bool        foundBufs,
                foundDescs,
                foundIOLocks,
                foundBufCkpt;

    /* Align descriptors to a cacheline boundary. */
    BufferDescriptors = (BufferDescPadded *)
        ShmemInitStruct("Buffer Descriptors",
                        NBuffers * sizeof(BufferDescPadded),
                        &foundDescs);

	if (enable_buffer_mprotect)
	{
		BufferBlocks = (char *)
			ShmemInitStruct("Buffer Blocks",
						NBuffers * (Size) BLCKSZ + BLCKSZ, &foundBufs);
		BufferBlocks = (char *) BLOCKALIGN(BufferBlocks);
	}
	else
	{
    BufferBlocks = (char *)
        ShmemInitStruct("Buffer Blocks",
                        NBuffers * (Size) BLCKSZ, &foundBufs);
	}

    /* Align lwlocks to cacheline boundary */
    BufferIOLWLockArray = (LWLockMinimallyPadded *)
        ShmemInitStruct("Buffer IO Locks",
                        NBuffers * (Size) sizeof(LWLockMinimallyPadded),
                        &foundIOLocks);

    LWLockRegisterTranche(LWTRANCHE_BUFFER_IO_IN_PROGRESS, "buffer_io");
    LWLockRegisterTranche(LWTRANCHE_BUFFER_CONTENT, "buffer_content");

    /*
     * The array used to sort to-be-checkpointed buffer ids is located in
     * shared memory, to avoid having to allocate significant amounts of
     * memory at runtime. As that'd be in the middle of a checkpoint, or when
     * the checkpointer is restarted, memory allocation failures would be
     * painful.
     */
    CkptBufferIds = (CkptSortItem *)
        ShmemInitStruct("Checkpoint BufferIds",
                        NBuffers * sizeof(CkptSortItem), &foundBufCkpt);

    if (foundDescs || foundBufs || foundIOLocks || foundBufCkpt)
    {
        /* should find all of these, or none of them */
        Assert(foundDescs && foundBufs && foundIOLocks && foundBufCkpt);
        /* note: this path is only taken in EXEC_BACKEND case */
    }
    else
    {
        int            i;

        /*
         * Initialize all the buffer headers.
         */
        for (i = 0; i < NBuffers; i++)
        {
            BufferDesc *buf = GetBufferDescriptor(i);

            CLEAR_BUFFERTAG(buf->tag);

            pg_atomic_init_u32(&buf->state, 0);

            buf->wait_backend_pid = 0;

            buf->buf_id = i;

            /*
             * Initially link all the buffers together as unused. Subsequent
             * management of this list is done by freelist.c.
             */
            buf->freeNext = i + 1;

			BufEnableMemoryProtection(BufferBlocks + i * BLCKSZ, false);

            LWLockInitialize(BufferDescriptorGetContentLock(buf),
                             LWTRANCHE_BUFFER_CONTENT);

            LWLockInitialize(BufferDescriptorGetIOLock(buf),
                             LWTRANCHE_BUFFER_IO_IN_PROGRESS);
        }

        /* Correct last entry of linked list */
        GetBufferDescriptor(NBuffers - 1)->freeNext = FREENEXT_END_OF_LIST;
    }

    /* Init other shared buffer-management stuff */
    StrategyInitialize(!foundDescs);

    /* Initialize per-backend file flush context */
    WritebackContextInit(&BackendWritebackContext,
                         &backend_flush_after);
}

/*
 * BufferShmemSize
 *
 * compute the size of shared memory for the buffer pool including
 * data pages, buffer descriptors, hash tables, etc.
 */
Size
BufferShmemSize(void)
{
    Size        size = 0;

    /* size of buffer descriptors */
    size = add_size(size, mul_size(NBuffers, sizeof(BufferDescPadded)));
    /* to allow aligning buffer descriptors */
    size = add_size(size, PG_CACHE_LINE_SIZE);

    /* size of data pages */
    size = add_size(size, mul_size(NBuffers, BLCKSZ));

    /* size of stuff controlled by freelist.c */
    size = add_size(size, StrategyShmemSize());

    /*
     * It would be nice to include the I/O locks in the BufferDesc, but that
     * would increase the size of a BufferDesc to more than one cache line,
     * and benchmarking has shown that keeping every BufferDesc aligned on a
     * cache line boundary is important for performance.  So, instead, the
     * array of I/O locks is allocated in a separate tranche.  Because those
     * locks are not highly contentended, we lay out the array with minimal
     * padding.
     */
    size = add_size(size, mul_size(NBuffers, sizeof(LWLockMinimallyPadded)));
    /* to allow aligning the above */
    size = add_size(size, PG_CACHE_LINE_SIZE);

    /* size of checkpoint sort array in bufmgr.c */
    size = add_size(size, mul_size(NBuffers, sizeof(CkptSortItem)));

    return size;
}
#ifdef __OPENTENBASE__
/*
 * Preload shared buffer from shared memory to local memory
 */
void
WarmSharedBuffer(void)
{
    char     *page = NULL;
    int32    tmp   = 0;
    uint64   i     = 0;
    uint64   bufSize = (uint64)NBuffers * BLCKSZ;
    if (BufferBlocks)
    {
        #define LINUX_KERNEL_PAGE_SIZE 4096
        #if 0
        elog(LOG, "warm shared buffer start");
        #endif
        for (i = 0; i < bufSize; )
        {
            page = BufferBlocks + i;    
            
            /* read the first four bytes */
            tmp  = *((int*)page);
            i += LINUX_KERNEL_PAGE_SIZE;
            if (tmp != 0) {};
        }    
        #if 0
        elog(LOG, "warm shared buffer end");
        #endif
    }
}
#endif
